package com.unionpay.fsas.encrypt;

import com.unionpay.fsas.encrypt.encoder.UnionEsscDemo;
import com.unionpay.fsas.encrypt.encoder.connect.UnionStart;
import com.unionpay.fsas.encrypt.encoder.message.UnionMessage;
import com.unionpay.fsas.encrypt.encoder.utils.Base64;
import com.unionpay.fsas.encrypt.encryptionmachine.EncryptionInfo;
import com.unionpay.fsas.encrypt.encryptionmachine.EncryptionResult;
import java.io.UnsupportedEncodingException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * @author caoqc
 * @Description: 江南科友加密机服务实现
 * @date 2021/11/22 14:18:44
 */

public class KeYouEncryptionMachineServiceImpl {

    private KeYouEncryptionMachineServiceImpl() {
    }

    public static KeYouEncryptionMachineServiceImpl getInstance() {
        return KeYou.INSTANCE;
    }

    static class KeYou {
        private static KeYouEncryptionMachineServiceImpl INSTANCE = new KeYouEncryptionMachineServiceImpl();
    }

    private static final String ENCRYPTION_SUCCESS_CODE = "000000";

    private static UnionStart api;
    private static UnionEsscDemo demo;

    private Logger logger = LoggerFactory.getLogger(getClass());

    private void startConfigure() {
        try {
            if (api != null && demo != null) {
                return;
            }
            api = new UnionStart("D:/workspace/myproduct/Sample_FundSettlement/assets");
            demo = new UnionEsscDemo(api);
            if (api == null || demo == null) {
                logger.error("江南科友加密机配置启动失败");
                throw new Exception("江南科友加密机配置启动失败");
            }
        } catch (Exception e) {
            logger.error("江南科友加密机配置启动异常", e);
        }
    }

    public EncryptionResult sensitiveInfoEncryption(String certKey, EncryptionInfo encryptionInfo) {
        startConfigure();
        String sensitiveInfo = "<ExpDt>" + encryptionInfo.getEncryptionInfo4KeYou().getExpireDate() + "</ExpDt>" +
                "<CvnNo>" + encryptionInfo.getEncryptionInfo4KeYou().getCvn() + "</CvnNo>";
        try {
            UnionMessage res = demo.servEA32(certKey, encodeString(sensitiveInfo));
            checkResult(res);
            String encodeData = res.getBodyField("encinfo");
            String encKey = res.getBodyField("enckey");
            return new EncryptionResult(encodeData, encKey);

        } catch (Exception e) {
            logger.error("江南科友加密机加密异常:{}", e);
            throw new RuntimeException("江南科友加密机加密异常" + e.getMessage());
        }
    }

    public String sensitiveInfoDecode(String certKey, String sensitiveInfo, String secretKey) {
        startConfigure();
        try {
            UnionMessage res = demo.servEA33(certKey, encodeString(secretKey), encodeString(sensitiveInfo));
            checkResult(res);
            String sensinfo = res.getBodyField("sensinfo");
            return new String(Base64.getDecoder().decode(sensinfo));
        } catch (Exception e) {
            logger.error("江南科友加密机信息解密异常:{}", e);
            throw new RuntimeException("江南科友加密机信息解密失败");
        }
    }


    public boolean veritySign(String certKey, String msg, String sign, String signSn) {
        startConfigure();
        try {
            //UnionMessage res = demo.servEA31(certKey, signSn, encodeString(msg), sign);


            /**
             * E171 公钥验签
             *
             * @param mode         模式
             * @param keyName      公开密钥名称
             * @param pkValue      公钥
             * @param algorithmID  算法标识
             * @param pkExponent   公钥指数
             * @param dataFillMode 数据补位方式
             * @param hashID       HASH算法标识
             * @param userID       用户标识
             * @param data         签名数据
             * @param sign         签名
             * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
             */
            String mode = "";
            String keyName = "";
            String pkValue = "";
            String algorithmID = "";
            String pkExponent = "";
            String dataFillMode = "";
            String hashID = "";
            String userID = "";
            String data = "";
            UnionMessage res = demo.servE171(mode, keyName, pkValue, algorithmID, pkExponent, dataFillMode, hashID,
                    userID, data, sign);
            checkResult(res);
            return Boolean.TRUE;
        } catch (Exception e) {
            logger.info("江南科友加密机签名验证异常:{}", e);
            throw new RuntimeException("江南科友加密机签名验证异常");
        }
    }

    public String sign(String certKey, String signSn, String msg) {
        startConfigure();
        try {

            /**
             * E170 私钥签名
             *
             * @param keyName      密钥名称
             * @param dataFillMode 数据补位方式
             * @param hashID       HASH算法标识
             * @param userID       用户标识
             * @param data         签名数据
             * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
             * 获取响应信息：
             * {@code string = sign} 	：签名
             */

            String keyName = certKey;
            String dataFillMode = "";
            String hashID = "03";
            String userID = signSn;
            String data = msg;

            UnionMessage res = demo.servE170(keyName, dataFillMode, hashID, userID, data);

            checkResult(res);
            logger.info("签名成功:{}", res.getBodyField("sign"));
            return res.getBodyField("sign");
        } catch (Exception e) {
            logger.error("江南科友加密机签名异常:{}", e);
            throw new RuntimeException("江南科友加密机签名异常");
        }
    }

    public String compute(String certKey, String signSn, String msg) {
        startConfigure();
        try {
            /**
             * E180 计算数据摘要
             *
             * @param data     摘要数据
             * @param dataType 数据类型
             *                 (可选：默认为1 1：asc数据 2：十六进制数据（数据带有不可见字符时，先转为十六进制，后台再转回去)
             * @param hashID   Hash算法标识
             * @param mode     SM3摘要模式
             * @param pk       公钥名称或密钥值
             * @param userID   用户ID
             * @return 返回 {@link UnionMessage} 对象，获取数据方法在该对象注释中已经说明。
             * 获取响应信息：
             * {@code string = digest} 	：摘要
             */
            String hashID = "4";
            String userID = signSn;
            String data = msg;
            String dataType = "2";
            String mode = "0";
            String pk = certKey;
            UnionMessage res = demo.servE180(data, dataType, hashID, mode, pk, userID);

            checkResult(res);
            logger.info("获取hax值成功:{}", res.getBodyField("digest"));
            return res.getBodyField("digest");
        } catch (Exception e) {
            logger.error("江南科友加密机获取hax值成功异常:{}", e);
            throw new RuntimeException("江南科友加密机获取hax值成功异常");
        }
    }

    private void checkResult(UnionMessage res) {
        if (null == res) {
            throw new RuntimeException("江南科友加密机业务处理失败");
        }
        if (!ENCRYPTION_SUCCESS_CODE.equals(res.getHeadField("responsecode"))) {
            throw new RuntimeException("江南科友加密机业务处理失败【code:" + res.getHeadField("responsecode") +
                    ",remark:" + res.getHeadField("responseremark") + "】");
        }
    }

    private String encodeString(String info) throws UnsupportedEncodingException {
        return Base64.getEncoder().encodeToString(info.getBytes("UTF-8"));
    }


}
